
// 块级作用域，从babel编译的代码中可以发现名字被加了下划线作为前缀，可以理解为一种语法糖吧。
'use strict';

{
  var _a = 10;
  var b = 1;
  console.log(_a); //10
  console.log(b); //1
}
console.log(a); // undefined
console.log(b); // 1

// 循环计数器，从babel编译好的代码可以发现是利用函数闭包来实现的
// 好处在于所设置的变量只在for的块作用域内有效，可以避免大量变量重复的问题
var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10

var a = [];

var _loop = function (_i) {
  a[_i] = function () {
    console.log(_i);
  };
};

for (var _i = 0; _i < 10; _i++) {
  _loop(_i);
}
a[6](); // 6

// ES6规定暂时性死区和不存在变量提升，主要是为了减少运行时错误，防止在变量声明前就使用这个变量，从而导致意料之外的行为。
// 这样的错误在ES5是很常见的，现在有了这种规定，避免此类错误就很容易了。
function do_something() {
  console.log(a); // ReferenceError
  var a = 1;
}
do_something();

// 暂时性死区。在代码块内，使用let命令声明变量之前，该变量都是不可用的。
if (true) {
  _tmp = 'abc'; // ReferenceError
  console.log(_tmp); // ReferenceError

  var _tmp = undefined;
  console.log(_tmp); // undefined

  _tmp = 123;
  console.log(_tmp); // 123
}

// 函数的作用域是其声明时所在的作用域。
var foo1 = 'outer';

function bar1() {
  var func = arguments.length <= 0 || arguments[0] === undefined ? function (x) {
    return foo1;
  } : arguments[0];
  return (function () {
    var foo1 = 'inner';
    console.log(func()); // outer
  })();
}

bar1();

//与上面相同效果
var foo2 = 'outer';
var f = function f(x) {
  return foo2;
};

function bar2() {
  var func = arguments.length <= 0 || arguments[0] === undefined ? f : arguments[0];

  var foo2 = 'inner';
  console.log(func()); // outer
}

bar2();

// 不允许重复声明
// 报错
function func1() {
  var a = 10;
  //var a = 1;
}

// 报错
function func2() {
  var a = 10;
  //let a = 1;
}

// 报错
function func3(arg) {}
//let arg;

// 不报错
function func4(arg) {
  {
    var _arg = undefined;
  }
}

// 总结下来let主要有以下几个特点：1.块级作用域 2.没有变量提升（存在暂时性死区） 3.不能重复声明
// 目的是：限制了变量的作用范围，避免大量程序因作用域而造成的错误